package com.rico.smart.socket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rico.smart.message.ImageMessage;
import com.rico.smart.message.UserLoginMessage;
import com.rico.smart.message.IMessage;
import com.rico.smart.message.MessageType;
import com.rico.smart.message.MsgMessage;
import com.rico.smart.utils.IByteBuffer;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.nio.charset.Charset;

import static com.rico.smart.message.MessageType.TEST;


public class SocketConnection {

    protected MessageHandler messageHandler;

    private boolean isConnected=false;

    private IMessage recvMessage;

    private Socket socket;

    private Long messageId;

    public SocketConnection(Socket socket){
        this.socket=socket;
        isConnected=true;
        initSocket();
    }

    public synchronized IMessage sendMessageAndWaitRespone(IMessage message,int waitTime)throws IOException{
        if(message.getMessageId()==null){
            message.setMessageId(System.currentTimeMillis());
        }
        messageId=message.getMessageId();
        this.sendMessage(message);
        if(recvMessage==null){
            try {
                this.wait(waitTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        IMessage iMessage= recvMessage;
        recvMessage=null;
        return iMessage;
    }

    public synchronized void sendMessage(IMessage message) throws IOException {
        try {
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            DataOutputStream dos=new DataOutputStream(bout);
            byte bytes[]=message.toBytes();
            dos.writeInt(bytes.length);
            //dos.writeInt(message.getClientType());
            //dos.writeInt(message.getMessageType().ordinal());
            dos.write(bytes);
            dos.flush();
            socket.getOutputStream().write(bout.toByteArray());
            socket.getOutputStream().flush();
            dos.close();
        } catch (IOException e) {
            e.printStackTrace();
            isConnected=false;
            throw new IOException(e);
        }
    }

    public IMessage toMessage(InputStream in,IByteBuffer buf) throws UnsupportedEncodingException {
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);

        String messageJson=new String(req,"UTF-8");
        JSONObject json=JSON.parseObject(messageJson);

        String messageTypeName=json.getString("messageType");
        MessageType messageType=MessageType.valueOf(messageTypeName);
        IMessage message = null;
        try {
            switch (messageType){
                case TEST:

                    break;
                case LOGIN:
                    message= JSON.parseObject(new ByteArrayInputStream(req), Charset.forName("UTF-8"),UserLoginMessage.class);
                    break;
                case IMAGE:
                    message= JSON.parseObject(new ByteArrayInputStream(req), ImageMessage.class);
                    break;
                default:
                    message=JSON.parseObject(new ByteArrayInputStream(req), Charset.forName("UTF-8"),MsgMessage.class);
                    break;
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return message;
    }

    public void setMessageHandler(MessageHandler messageHandler) {
        this.messageHandler = messageHandler;
    }

    private void initSocket(){
       new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    DataInputStream dis=new DataInputStream(socket.getInputStream());
                    while(isConnected){
                        try {
                            handlerSocketInputStream(dis);
                        }catch (Exception e){
                            isConnected=false;
                            e.printStackTrace();
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    if(messageHandler!=null){
                        messageHandler.onReceivedMessageError(SocketConnection.this,e);
                    }
                }
            }
        }).start();
    }

    private void handlerSocketInputStream(DataInputStream dis) throws IOException {
        int len=dis.readInt();
        byte readBytes[]=new byte[1024];
        ByteArrayOutputStream baos=new ByteArrayOutputStream();
        while(len>0) {
            if(len<readBytes.length){
                readBytes=new byte[len];
            }
            int rlen=dis.read(readBytes);
            baos.write(readBytes,0,rlen);
            len=len-rlen;
        }
        IByteBuffer buf =new IByteBuffer(baos.toByteArray());
        IMessage iMessage=toMessage(dis,buf);
        if(iMessage.getRespMessageId()!=null&&iMessage.getRespMessageId().equals(messageId)){
            this.recvMessage=iMessage;
            synchronized (SocketConnection.this) {
                SocketConnection.this.notifyAll();
            }
        }
        if (messageHandler != null) {
            if(iMessage.getMessageType()!= TEST) {
                messageHandler.onMessageReceived(SocketConnection.this, iMessage);
            }
        }
    }

    public boolean isConnected() {
        return isConnected;
    }

    public void setConnected(boolean connected) {
        isConnected = connected;
    }

}
